/**
 * 返回XHR对象。
 * 注意 url 必须以http开头的完整路径。
 * @param {String} referer 定义 HTTP-REFERER 变量，请求方说明，例如 http://www.163.com/
 * @return {MSXML.ServerXMLHTTP}
 */
$$.xhr = function (asyncCallback, referer){
	var xhr = typeof XMLHttpRequest != 'undefined' && new XMLHttpRequest();
	var activeX;
	if(!xhr){
		activeX = [
			 'MSXML2.ServerXMLHTTP.5.0'
			,'MSXML2.ServerXMLHTTP.3.0'
			,'MSXML2.ServerXMLHTTP'
			,'MSXML2.XMLHTTP.3.0'
			,'MSXML2.XMLHTTP'
			,'Microsoft.XMLHTTP'
		];
		
		// try 是写在 for 循环里面的，
		// 每当遇到一个try语句，异常的框架就放到堆栈上面，直到所有的try语句都完成。
		// 如果下一级的try语句没有对某种异常进行处理，堆栈就会展开，直到遇到有处理这种异常的try语句。
		// 这是十分浪费性能的做法，但暂时又找不到恰当的解决方式，于是采用了这种方法。
		for (var i = 0, j = activeX.length; i < j; ++i) {
			try {
				xhr = new ActiveXObject(activeX[i]);
				break;
			} catch (e){
			}
		}
	}
	
	if(!xhr){
		throw '没有XHR组件！';
	}
	
	if(activeX && activeX[i].indexOf('Server') != -1){
		// 设置超时
		// Timeouts in ms for parts of communication: resolve, connect, send (per packet), receive (per packet)
		xhr.setTimeouts(30000, 30000, 30000, 30000); 
		      
		// 忽略 SSL Ignore all SSL errors
		xhr.setOption(2, 13056); 
	}	
    

	
	if(asyncCallback && typeof(asyncCallback) == 'function'){
		xhr.onreadystatechange = function(){
			if (xhr.readyState == 0){
			}else if (xhr.readyState == 1){
			}else if (xhr.readyState == 2){
			}else if (xhr.readyState == 3){
			}else if (xhr.readyState == 4 && xhr.status == 200){
				asyncCallback(xhr);
		    }else if(xhr.readyState == 4 && xhr.status != 200){
		        // Non-OK HTTP response
		        var text = "Http Error: {0} {1}\nFailed to grab page data from: {2}";
		        text = text.format(xhr.status, xhr.statusText);
		        throw text;
		    }else if(xhr.readyState == 4){
		    }
		}
	}
	
	return xhr;
}

$$.request = function(cfg){
	var xhr = $$.xhr(cfg.fn);
	
	var method = "GET";
	if(cfg.isPost){
		method = "POST"
	}
	
	if(cfg.method){
		method = cfg.method.toUpperCase();
	}
	
	
	method == 'POST' && xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");

	var isAysc = typeof cfg.isAysc == 'undefined' ? true /* 默认是异步的 */: cfg.isAys;
	isAysc = !!isAysc;
	
	xhr.open(method, cfg.url, isAysc);
	
	isAysc
	
    cfg.referer 	&& xhr.setRequestHeader("Referer", 		cfg.referer);
    cfg.contentType && xhr.setRequestHeader("Content-Type", cfg.contentType); // you may set xhr.setRequestHeader("Accept", "text/json");
	
	xhr.send(null);

	return xhr;
}

$$.xml = {
	doc : function(){
		var doc;
		
		/**
		 * ActiveX Objects for importing XML (IE only)
		 * @type Array
		 */
		var MSXML = [
			"Msxml2.DOMDocument.6.0",
			"Msxml2.DOMDocument.5.0", 
			"Msxml2.DOMDocument.4.0", 
			"Msxml2.DOMDocument.3.0", 
			"MSXML2.DOMDocument", 
			"Microsoft.XMLDOM"   
		];
		
		for(var i = 0, j = MSXML.length; i < j; i++){
			try{
				doc = new ActiveXObject(MSXML[i]);
				break;
			}catch(e){}
		}
		
		return doc;	
	}

	/**
	 * 保存 XML 对象为 XML 文本文件。
	 * 注意：Server Side Only
	 * @param	{XMLDocument}	xmlDoc		XML 文档对象本身。
	 * @param	{String}		xmlFilePath	可选的。XML 文档的真实磁盘路径。
	 * @return	{Boolean}					是否操作成功。
	 */
	,save : function(xmlDoc, xmlFilePath){
	 	xmlFilePath = xmlFilePath || xmlDoc.url.replace(/file:\/\/\//,'');
	 	// make a clone
		var	saver = this.doc();
			saver.loadXML(xmlDoc.xml);
			
		if(	saver.readyState == 4 && saver.parsed){
			saver.save(xmlFilePath);
		}
		
		return true;
	}	
};

$$.file = {
	/** 
	 * 打开文件，读取其内容，返回文本的格式。
	 * @param	{String}	path		文件路径
	 * @param	{String}	sCharset	指定字符集
	 * @return	{String} 				文本内容
	 */
	
	read_File_with_Chartset : function (filename, sCharset){
		var stream = new ActiveXObject('adodb.stream');
		var fileContent;
	
		with(stream){
			type = 2;// 1－二进制，2－文本
			mode = 3;// 1－读，2－写，3－读写
			open();
		
			if (!sCharset) {
				try{
					charset = "437"; // why try, cause some bug now
				}catch(e){}
				loadFromFile(filename);
				
				// get the BOM(byte order mark) or escape(ReadText(2)) is fine?
				switch (escape(readText(2).replace(/\s/g, ''))) {
					case "%3Ca" :
					case "%3Cd" :
					case "%3C%3F" :
					case "%u2229%u2557" :	// 0xEF,0xBB => UTF-8
						sCharset = "UTF-8";
						break;
					case "%A0%u25A0" :		// 0xFF,0xFE => Unicode
					case "%u25A0%A0" :		// 0xFE,0xFF => Unicode big endian
						sCharset = "Unicode";
						break;
					default :
						// 判断不出来就使用GBK，这样可以在大多数情况下正确处理中文
						sCharset = "GBK";	
				}
				close();
				open();
			}
			charset = sCharset;
			loadFromFile(filename);
			fileContent = new String(readText());
			fileContent.charset = sCharset;
			close();
		}
		return fileContent;
	}
	
	/**
	 * 将数据保存到磁盘上。可支持文本数据和二进制数据。
	 * @param 	{String} 	path 		文件路径。
	 * @param 	{String} 	data 		要写入的数据，可以是二进制对象。
	 * @param 	{Boolean} 	isBinary	是否为二进制数据。
	 * @param 	{Boolean} 	isMapPath	是否送入相对路径。True 的话把虚拟路径还原为真实的磁盘路径。
	 * @return 	{Boolean} 	True		表示操作成功。
	 */
	,write : function(path, data, isBinary, chartset){
	    with(new ActiveXObject("Adodb.Stream")){
	        type = isBinary ? 1 : 2;
	        if (!chartset && !isBinary){
				charset = "utf-8";
	        }
	        if (chartset){
				charset = "GB2312";
	        }
	        try {
				open();
				if(!isBinary){
					writeText(data);
				}else{
					write(data);
				} 
				saveToFile(path, 2);
				
				return true;
	        }catch(e){
				throw e;
	        } finally {
				close();
	        }
	    }
	    
	    return true;
	}
	
	/**
	 * 送入一个文件磁盘地址，使用ADODB.Stream组件下载。
	 * 文件大小有限制？ Need Huge Server RAM！！
	 * 隐藏下载地址及防盗代码。 防盗链 
	 * @param {String} filePath
	 * @return {Boolean} 是否传送成功。
	 */
	,downFile : function(data){
		var fileObj, fileSize;
			
		with(new ActiveXObject("Scripting.FileSystemObject")){
			fileObj = getFile(filePath);
			if(!fileObj){
				throw '目标文件不存在！';
			}
			fileSize = fileObj.size;
		}
		
	    with(new ActiveXObject("ADODB.Stream")){
	        open();
	        type = 1;
	        loadFromFile(filePath);
		    Response.addHeader("Content-DIsposItIon", "attachment; FIlename=" + F.Name);
		    Response.addHeader("Content-Length", IntFilelength);
	        Response.Buffer      = True
		    Response.CharSet     = "UTF-8";
		    Response.ContentType = "application/x-download";
    		Response.clear();
		    Response.binaryWrite(Read());
		    Response.flush();
		    Close();
	    }
	    return true;
	}
	
	,base64EncodeText: function (TextStr) {
		var xml_dom = new ActiveXObject("MSXML2.DOMDocument");
		var ado_stream = new ActiveXObject("ADODB.Stream");
		var tmpNode = xml_dom.createElement("tmpNode");
		
		tmpNode.dataType = "bin.base64";
		ado_stream.Charset = "gb2312";
		ado_stream.Type = 2;// 1=adTypeBinary 2=adTypeText
		if (ado_stream.state == 0) {// 0=adStateClosed 1=adStateOpen
			ado_stream.Open();
		}
		ado_stream.WriteText(TextStr);
		ado_stream.Position = 0;
		ado_stream.Type = 1;// 1=adTypeBinary 2=adTypeText
		tmpNode.nodeTypedValue = ado_stream.Read(-1);// -1=adReadAll
		ado_stream.Close();
		return tmpNode.text;
	}
};

$$.file.toString = $$.file.read_File_with_Chartset.delegate(null, 'UTF-8');

$$.db = {
	connect : function(cfg){
		var connectObj = new ActiveXObject("ADODB.CONNECTION"); // 数据库对象
		var 
		 dbType			// 链接数据库的类型，是 Access 呢？MySQL 呢？还是 SQLServer？
		,dbPath 		// 数据库文件的路径，如果通过磁盘访问的话。
		,connectStr;	// 链接数据库的字符串。
		
		if(cfg){
			dbType = cfg.dbType;
		}else if(!cfg && $$.cfg){
			dbType = $$.cfg.edk_dbType.toLowerCase();
		}else{
			dbType = 'access';	// 默认为MS Access数据库
		}
		
		switch(dbType){
			case 'access' :
				if(cfg && cfg.dbPath){
					dbPath = cfg.dbPath;
				}else if(!cfg && $$.cfg){
				    dbPath = $$.cfg.edk_isDebugging ? $$.cfg.edk_dbFilePath_Test : Server.mappath($$.cfg.edk_dbFilePath);
				}
		        connectStr = "DBQ={0};DefaultDir=;DRIVER={Microsoft Access Driver (*.mdb)};".format(dbPath);
			break;
			case 'sqlservver' :
				throw '尚未实现';
			case 'mysql' :
				// 使用driver模式不用有空格在连接字符串中
				connectStr = "DRIVER={mysql odbc 5.1 driver};SERVER={0};PORT={1};UID={2};PASSWORD={3};DATABASE={4};OPTION=3";
				connectStr = connectStr.format('localhost', 3306, 'root' ,'123', 'test');            		
			break;
			case 'sqlite' :
				dbPath = 'd:\\test.db';
				connectStr = "DRIVER={SQLite3 ODBC Driver};Database=" + dbPath;
			break;
			default:
				throw '非法数据库连接类型！';
		};

		connectObj.open(connectStr);
		
		return connectObj;
	}
};